package org.dhis2.data.dhislogic

import java.util.Calendar
import java.util.Date
import org.dhis2.utils.Constants
import org.dhis2.utils.DateUtils
import org.hisp.dhis.android.core.enrollment.Enrollment
import org.hisp.dhis.android.core.program.ProgramStage

class EnrollmentEventGenerator(
    private val generatorRepository: EnrollmentEventGeneratorRepository
) {

    fun generateEnrollmentEvents(enrollmentUid: String): Pair<String, String?> {
        val enrollment = generatorRepository.enrollment(enrollmentUid)
        autogeneratedEvents(enrollment)
        return firstStageToOpen(enrollment)
    }

    private fun autogeneratedEvents(enrollment: Enrollment) {
        val autogeneratedEvents = generatorRepository.enrollmentAutogeneratedEvents(
            enrollment.uid(),
            enrollment.program()!!
        )
        autogeneratedEvents.forEach { programStage ->
            generateEvent(enrollment, programStage) {
                when (programStage.reportDateToUse()) {
                    Constants.ENROLLMENT_DATE -> enrollment.enrollmentDate()
                    Constants.INCIDENT_DATE -> enrollment.incidentDate()
                    else -> null
                }
            }
        }
    }

    private fun firstStageToOpen(enrollment: Enrollment): Pair<String, String?> {
        val program = generatorRepository.enrollmentProgram(enrollment.program()!!)
        val stageToOpen = if (program.useFirstStageDuringRegistration() == true) {
            generatorRepository.firstStagesInProgram(enrollment.program()!!)
        } else {
            generatorRepository.firstOpenAfterEnrollmentStage(enrollment.program()!!)
        }
        return stageToOpen?.let {
            checkIfEventExist(enrollment, stageToOpen)
        } ?: Pair(enrollment.uid(), null)
    }

    private fun checkIfEventExist(
        enrollment: Enrollment,
        programStage: ProgramStage
    ): Pair<String, String> {
        if (!generatorRepository.eventExistInEnrollment(enrollment.uid(), programStage.uid())) {
            generateEvent(enrollment, programStage) {
                val generateByEnrollmentDate = programStage.generatedByEnrollmentDate() ?: false
                if (!generateByEnrollmentDate && enrollment.incidentDate() != null) {
                    enrollment.incidentDate()
                } else {
                    enrollment.enrollmentDate()
                }
            }
        }
        val eventUidToOpen = generatorRepository.eventUidInEnrollment(
            enrollment.uid(), programStage.uid()
        )
        return Pair(enrollment.uid(), eventUidToOpen)
    }

    private fun generateEvent(
        enrollment: Enrollment,
        programStage: ProgramStage,
        dateToUse: () -> Date?
    ) {
        val eventUid = generatorRepository.addEvent(
            enrollment.uid(),
            enrollment.program()!!,
            programStage.uid(),
            enrollment.organisationUnit()!!
        )

        val hideDueDate = programStage.hideDueDate() ?: false
        val periodType = programStage.periodType()
        val minDaysFromStart = programStage.minDaysFromStart() ?: 0
        val calendar = DateUtils.getInstance().calendar

        val now = Calendar.getInstance().time
        dateToUse()?.let { date -> calendar.time = date }

        calendar.set(Calendar.HOUR_OF_DAY, 0)
        calendar.set(Calendar.MINUTE, 0)
        calendar.set(Calendar.SECOND, 0)
        calendar.set(Calendar.MILLISECOND, 0)
        calendar.add(Calendar.DATE, minDaysFromStart)
        var eventDate = calendar.time

        periodType?.let { eventDate = generatorRepository.periodStartingDate(it, eventDate) }

        val isSchedule = eventDate.after(now) && !hideDueDate
        generatorRepository.setEventDate(eventUid, isSchedule, eventDate)
    }
}
